package de.hpi.generator.helpers;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.lang.reflect.Modifier;

public class JSONHelper {

    public static Object deserializeObject(String aFileName, Class<?> aObjectClass) throws Exception {
        Gson aGson = new GsonBuilder().excludeFieldsWithModifiers(Modifier.PRIVATE, Modifier.PROTECTED).create();
        String aJsonText = FileHelper.readTextFromFile(aFileName);
        aJsonText = normalizeJsonText(aJsonText);
        return aGson.fromJson(aJsonText, aObjectClass);
    }

    /**
     * Normalize JSON text. Removes new lines, tabulation, spaces, comment lines
     * 
     * @param aJsonText
     *            an input json text
     * @return normalized json text
     */
    private static String normalizeJsonText(String aJsonText) {
        StringBuilder aStringBuilder = new StringBuilder();

        for (String aLine : aJsonText.split("\n")) {
            aLine = aLine.replace("\t", "").replace(" ", "");
            // remove comments
            if (aLine.startsWith("#")) {
                aLine = "";
            }
            aStringBuilder.append(aLine);
        }
        return aStringBuilder.toString();
    }

    public static void toJSON(Object aObject, String aFileName) throws Exception {
        Gson gson = new Gson();
        String result = formatJsonText(gson.toJson(aObject));

        FileHelper.writeTextToFile(result, aFileName, false);
    }

    private static String formatJsonText(String aJsonText) {        
        StringBuilder result = new StringBuilder();
        int depth = 0;
        boolean splitParamsMode = true;

        for (char aChar : aJsonText.toCharArray()) {

            if (aChar == '"') {
                splitParamsMode ^= true;
            }

            if (aChar == '{') {
                depth = processOpenBrace(result, depth, '{');
                continue;
            }

            if (aChar == '}') {
                depth = processCloseBrace(depth, result, '}');
                continue;
            }

            if (aChar == '[') {
                appendTabbedNewLine(result, depth);
                depth++;
                result.append(aChar);
                continue;
            }

            if (aChar == ']') {
                depth = processCloseBrace(depth, result, ']');
                continue;
            }

            if (aChar == ',' && splitParamsMode == true) {
                result.append(aChar);
                appendTabbedNewLine(result, depth);
                continue;
            }

            result.append(aChar);
        }

        return result.toString();
    }

    private static int processCloseBrace(int depth, StringBuilder result, char aBraceChar) {
        depth--;
        appendTabbedNewLine(result, depth);
        result.append(aBraceChar);
        return depth;
    }

    private static int processOpenBrace(StringBuilder result, int depth, char aBraceChar) {
        appendTabbedNewLine(result, depth);
        depth++;
        result.append(aBraceChar);
        appendTabbedNewLine(result, depth);
        return depth;
    }

    private static void appendTabbedNewLine(StringBuilder result, int depth) {
        if (result.length() > 0) {
            result.append("\n");
        }
        for (int aTabs = 0; aTabs < depth; aTabs++) {
            result.append(" ");
        }
    }
}